Skip to content

feat(theme): configurable schema expansion level (#1222)#1449

Merged
sserrata merged 10 commits into
mainfrom
feat/schema-expansion-level-1222
May 6, 2026
Merged

feat(theme): configurable schema expansion level (#1222)#1449
sserrata merged 10 commits into
mainfrom
feat/schema-expansion-level-1222

Conversation

@sserrata
Copy link
Copy Markdown
Member

@sserrata sserrata commented May 4, 2026

Summary

Closes #1222.

Adds an opt-in themeConfig.api.schemaExpansion option that controls the default expansion depth of nested request/response schema trees, plus an inline icon control next to each schema header that lets readers change the depth at view time. The selected depth is persisted in localStorage.

Inspired by Redoc's schemaExpansionLevel — useful primarily for envelope-wrapped APIs (e.g. { status, meta, data: {...} }) where readers otherwise have to manually click into every data property.

How it works

  • New SchemaExpansionProvider and SchemaDepthProvider contexts thread the active expansion level and per-node depth without prop-drilling through the recursive Schema renderer.
  • SchemaNodeDetails opens its <Details> when depth < level. Keying on level forces a remount on level change so the control feels responsive without losing per-row click toggling afterward.
  • New SchemaExpansionControl renders a small icon trigger inline with the Body / Schema headers; clicking opens a compact popover with depth options including "All". The popover is positioned with position: fixed (computed from the trigger's bounding rect) so it isn't clipped when the surrounding <details> is collapsed, and closes on scroll to stay visually anchored.
  • Behavior is unchanged when schemaExpansion is unset — no control rendered, schemas remain collapsed by default.

Config

themeConfig: {
  api: {
    schemaExpansion: {
      enabled: true,         // show the depth control (default: false)
      default: 1,            // initial depth — "all" supported (default: 0)
      max: 4,                // max depth offered by the control (default: 4)
      persist: true,         // remember last choice in localStorage (default: true)
    },
  },
}

Accessibility & i18n

  • <button> trigger with aria-haspopup="menu", aria-expanded, aria-controls, aria-label, and a native title tooltip.
  • Popover has role="menu" and its own aria-label; options are role="menuitemradio" with aria-checked and a translated per-option label ("Expand to depth N").
  • Focus moves to the active option on open; ← / → cycle through options; ESC closes and returns focus to the trigger.
  • All user-facing strings are routed through @docusaurus/Translate and registered in translationIds.ts under OPENAPI_SCHEMA_EXPANSION (theme.openapi.schema.expansion.*).

Files

  • packages/docusaurus-theme-openapi-docs/src/theme/SchemaExpansion/ — new context module, control component, BEM-style SCSS
  • packages/docusaurus-theme-openapi-docs/src/theme/Schema/index.tsxSchemaNodeDetails reads context, threads depth
  • packages/docusaurus-theme-openapi-docs/src/theme/{Request,Response}Schema/index.tsx — render the inline control
  • packages/docusaurus-theme-openapi-docs/src/theme/ApiItem/index.tsx — wraps the page in the provider
  • packages/docusaurus-theme-openapi-docs/src/theme/translationIds.ts — registers the new translation keys
  • packages/docusaurus-theme-openapi-docs/src/types.d.ts — types schemaExpansion on ThemeConfig.api
  • demo/examples/tests/schemaExpansion.yaml — envelope-wrapped fixture for manual exercise
  • demo/docusaurus.config.ts — enables the option in the demo

Test plan

  • Visit /docs/tests/get-user-envelope-wrapped — the depth icon appears to the right of the "SCHEMA" header
  • With default: 1, the top-level data property is open on load; deeper children are not
  • Clicking the icon opens a popover with 0 1 2 3 4 All; selecting a value adjusts the open depth across the tree
  • Selection persists across reloads (localStorage key docusaurus-openapi-schema-expansion-level)
  • Same control appears next to the "BODY" header on the create-user endpoint
  • Tab/arrow-key navigation works inside the popover; ESC closes and returns focus to the trigger
  • Removing schemaExpansion from the demo config hides the control and restores prior collapsed-by-default behavior
  • Mobile / narrow viewport: popover stays anchored to the trigger and doesn't overflow

🤖 Generated with Claude Code

Adds an opt-in `themeConfig.api.schemaExpansion` option that controls
the default expansion depth of nested request/response schema trees,
plus an inline icon control next to each schema header that lets
readers change the depth at view time. The selected depth is persisted
in localStorage.

- New `SchemaExpansionProvider` and `SchemaDepthProvider` contexts
  thread the active expansion level and per-node depth without
  prop-drilling through the recursive Schema renderer.
- `SchemaNodeDetails` opens its `<Details>` when `depth < level` and
  remounts on level change so the control feels responsive.
- New `SchemaExpansionControl` renders an icon trigger inline with the
  Body/Schema headers; clicking opens a compact popover with depth
  options including "All".
- Behavior is unchanged when `schemaExpansion` is unset.

Demo: enables the option and adds an envelope-wrapped fixture under
`demo/examples/tests/schemaExpansion.yaml` for manual exercise.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Size Change: +15.5 kB (+0.68%)

Total Size: 2.28 MB

📦 View Changed
Filename Size Change
demo/.docusaurus/docusaurus.config.mjs 16.4 kB +119 B (+0.73%)
demo/.docusaurus/globalData.json 72.3 kB +1.81 kB (+2.57%)
demo/.docusaurus/registry.js 105 kB +2.63 kB (+2.58%)
demo/.docusaurus/routes.js 98.9 kB +2.54 kB (+2.63%)
demo/.docusaurus/routesChunkNames.json 41.1 kB +1.05 kB (+2.63%)
demo/build/assets/css/styles.********.css 173 kB +2.12 kB (+1.24%)
demo/build/assets/js/main.********.js 672 kB +4.7 kB (+0.7%)
demo/build/assets/js/runtime~main.********.js 24.1 kB +540 B (+2.29%)
ℹ️ View Unchanged
Filename Size Change
demo/.docusaurus/codeTranslations.json 2 B 0 B
demo/.docusaurus/i18n.json 372 B 0 B
demo/.docusaurus/site-metadata.json 1.58 kB 0 B
demo/build/index.html 95.4 kB 0 B
demo/build/petstore/add-pet/index.html 30 kB 0 B
demo/build/petstore/create-user/index.html 24.7 kB 0 B
demo/build/petstore/create-users-with-array-input/index.html 24.8 kB 0 B
demo/build/petstore/create-users-with-list-input/index.html 24.8 kB 0 B
demo/build/petstore/delete-order/index.html 24.5 kB 0 B
demo/build/petstore/delete-pet/index.html 24.8 kB 0 B
demo/build/petstore/delete-user/index.html 25 kB 0 B
demo/build/petstore/find-pets-by-status/index.html 25.5 kB 0 B
demo/build/petstore/find-pets-by-tags/index.html 26.1 kB 0 B
demo/build/petstore/get-inventory/index.html 23.8 kB 0 B
demo/build/petstore/get-order-by-id/index.html 24.8 kB 0 B
demo/build/petstore/get-pet-by-id/index.html 25.6 kB 0 B
demo/build/petstore/get-user-by-name/index.html 25.1 kB 0 B
demo/build/petstore/login-user/index.html 25.6 kB 0 B
demo/build/petstore/logout-user/index.html 24.4 kB 0 B
demo/build/petstore/new-pet/index.html 25 kB 0 B
demo/build/petstore/pet/index.html 23.2 kB 0 B
demo/build/petstore/place-order/index.html 24 kB 0 B
demo/build/petstore/schemas/apiresponse/index.html 25.2 kB 0 B
demo/build/petstore/schemas/cat/index.html 39 kB +12 B (+0.03%)
demo/build/petstore/schemas/category/index.html 26.3 kB +6 B (+0.02%)
demo/build/petstore/schemas/dog/index.html 39.3 kB +12 B (+0.03%)
demo/build/petstore/schemas/honeybee/index.html 39.3 kB +12 B (+0.03%)
demo/build/petstore/schemas/id/index.html 23.4 kB 0 B
demo/build/petstore/schemas/order/index.html 27.3 kB 0 B
demo/build/petstore/schemas/pet/index.html 38.8 kB +12 B (+0.03%)
demo/build/petstore/schemas/tag/index.html 24.7 kB 0 B
demo/build/petstore/schemas/user/index.html 40.7 kB +6 B (+0.01%)
demo/build/petstore/store/index.html 22.2 kB 0 B
demo/build/petstore/subscribe-to-the-store-events/index.html 30.9 kB 0 B
demo/build/petstore/swagger-petstore-yaml/index.html 30.9 kB 0 B
demo/build/petstore/update-pet-with-form/index.html 25 kB 0 B
demo/build/petstore/update-pet/index.html 25.4 kB 0 B
demo/build/petstore/update-user/index.html 25 kB 0 B
demo/build/petstore/upload-file/index.html 24.8 kB 0 B
demo/build/petstore/user/index.html 22.9 kB 0 B

compressed-size-action

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Visit the preview URL for this PR (updated for commit 02cfa64):

https://docusaurus-openapi-36b86--pr1449-y9rk732r.web.app

(expires Tue, 12 May 2026 14:59:09 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: bf293780ee827f578864d92193b8c2866acd459f

Defaults to the standard arrow cursor on hover for a less clicky feel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lock the trigger button to a 22x22 box so the hover background and
click area match the icon, rather than stretching to the flex row's
height.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
sserrata and others added 2 commits May 4, 2026 17:35
When the surrounding <details> was collapsed, the absolutely-positioned
popover got clipped by the details element. Render the popover via a
portal to document.body with fixed positioning computed from the
trigger's bounding rect, and reposition on scroll/resize.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Avoid pulling in @types/react-dom by computing the trigger's bounding
rect and rendering the popover with position: fixed in-place. Fixed
positioning still escapes the surrounding details overflow, so the
menu is no longer clipped when the schema is collapsed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When the parent summary became a flex row, the h3/strong holding the
title and the REQUIRED span fell back to block layout and wrapped.
Make the title element itself an inline-flex with a small gap so the
title and the badge stay on one line and align vertically.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
sserrata and others added 3 commits May 5, 2026 10:23
Switch the aria-label / title from "Schema expansion depth" to a more
descriptive "Set how deep schemas auto-expand" so hovering the icon
explains what the control does.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Repositioning the popover during scroll lagged behind the trigger and
felt detached. Close the popover instead so it always appears anchored
at the moment of opening. Also revert the tooltip text to its original
"Schema expansion depth".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Centralize translation IDs under OPENAPI_SCHEMA_EXPANSION in
  translationIds.ts so they can be discovered alongside other theme
  strings and overridden via i18n catalogs.
- aria-haspopup="menu" (was "true") + aria-controls + popover id +
  aria-label on the menu so assistive tech announces it as a menu.
- Move focus to the active option when the menu opens; arrow-key
  navigation between options; ESC stops propagation so it doesn't
  also collapse a parent details element.
- Per-option aria-label "Expand to depth N" via translate() so screen
  readers get a full description instead of just a number.
- Class names already follow BEM (block, __element, --modifier).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`themeConfig.api.schemaExpansion.default` now reliably auto-expands to
the configured depth on every page load when `enabled` is false — the
provider previously kept reading from localStorage in that case, so a
stale value from a prior session where the control was enabled could
shadow the new default. Persistence is now implicitly off whenever the
control is hidden.

Also clarify the docstrings so it's discoverable that you can set just
`{ default: 1 }` to get auto-expansion without rendering the UI.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@sserrata sserrata merged commit 0cdec2e into main May 6, 2026
18 of 20 checks passed
@sserrata sserrata deleted the feat/schema-expansion-level-1222 branch May 6, 2026 13:22
sserrata added a commit that referenced this pull request May 6, 2026
Adds documentation for the new schemaExpansion theme option (introduced
in #1449) to intro.mdx and both READMEs, covering the nested enabled,
default, max, and persist fields plus an example config.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Default schema expansion level

1 participant